
#include "stdafx.hpp"
#include "bitmap.hpp"
#include <fstream>
#include <locale> //toupper()
#include <string>
#include <cstring> //memcpy()

bitmap::bitmap() : alright(false), data(0)
{   //empty
    //we begin in a fail state since load() hasn't been called yet.
}

bitmap::~bitmap()
{
    if(data)
    {
        delete [] data;
    	data = 0;
    }
}

bitmap::bitmap(const std::string & path) : alright(true), data(0)
{
    load(path); //will set failure if it fails.
}

bool bitmap::load(const std::string & path)
{
	if(data)
           throw std::string("This bitmap is already loaded.");
	std::ifstream fin(path.c_str(), std::ios::in | std::ios::binary);
	if(!fin)
	   return (alright = false);

	fin.seekg(OFFSET, std::ios::beg);
	fin.read((char*)(&offset), sizeof(uword));
	fin.seekg(WIDTH, std::ios::beg);
	fin.read((char*)(&w), sizeof(uword));
	fin.seekg(HEIGHT, std::ios::beg);
	fin.read((char*)(&h), sizeof(uword));
    fin.seekg(BPP, std::ios::beg);
	fin.read((char*)(&bpp), sizeof(uword));

	file_path = path;

	output << "\n--Bitmap Information--\n"
#ifndef NDEBUG
			  << "Offset: " << offset << '\n'
#endif
			  << "Width: " << w << '\n'
			  << "Height: " << h << std::endl;

    //Data copying section:

	pixelsz = bpp/bits_per_char;	 //num of chars in a pixel
    assert(bpp % bits_per_char == 0); //assert that chars actually divide a pixel! (this'll snag if a monochrome is used)
    printdb(pixelsz);
    printdb(w*h*pixelsz);
    data = new char[w*h*pixelsz];

    //Now we read in the bmp information, ignoring the padding space added by the format.
    const uword extra_bytes = ( w*(pixelsz) % sizeof(uword) ); //amount of padding after each row.
	fin.seekg(offset,std::ios::beg);
	for(unsigned int y = 0; y < h; ++y)
	{
		for(unsigned int chunk = 0; chunk < w*(pixelsz); chunk += pixelsz)
			fin.read(&data[w*y*pixelsz + chunk],pixelsz);
		fin.ignore(extra_bytes);
	}
	return alright = true;
}

uword bitmap::operator()(unsigned int x, unsigned int y) const
{
    //copies the three chars (that are the pixel) into a uword and returns the uword.
    uword ret = 0;
    std::memcpy( &ret, &data[ (w*y+x)*pixelsz ], pixelsz ); //See below.
    //In matrix:
    //	A B C
    //	D E F
    //	G H I
    //In memory (via the array 'data'):
    //	G H I D E F A B C
    return ret;
}

//Extracts a region from the bitmap. Encodes spans of color and their dielectric constants,
//skips the ignored color (perfect conductor).
//Function proceeds in verticle strips, pushing back a new 'span' for every consecutive
//sequence of the same color. Looks up that color's dielectric constant and puts it into the span.
//Creates a new span for every color change and every new column.

//Note: there is a bug with bitmaps having an odd-numbered width. Just keep it even for now.
grid::region bitmap::region(const std::string & path, const color & ignored)
{
	if(!alright)
	   throw std::string("bitmap::find_region() called while in a fail state.");

	bool output = ( path != std::string() );
	std::ofstream fout;	//This is where the ignored space will be mentioned
	if(output)
	   fout.open(path.c_str());

	using settings::colors; //a map
	grid::region reg;
	color prev_color, cur_color;
	for(uword x = 1; x < w-1; ++x)
	{
	    prev_color = cur_color = (*this)(x,1); //Here's a new column, so we'll need a new entry
	    if(cur_color == ignored)
            fout << x << ' ' << 1 << std::endl;
        else
			reg.push_back( grid::span(x,1,1,colors[cur_color]) );
	    for(uword y = 2; y < h-1; ++y)
	    {
	        cur_color = (*this)(x,y);
	        if(cur_color == ignored)
				fout << x << ' ' << y << std::endl;
	        else if(cur_color == prev_color)
				++(reg.back().dy);
            else
				reg.push_back( grid::span(x,y,1,colors[cur_color]) );
            prev_color = cur_color;
	    }// for y
	}// for x
	return reg;
}

grid::boundary bitmap::boundary(const color & ignored)
{
	//Finds the 'ignored' pixels closest to unignored ones
	//so that they can be set equal to their neighbors each
	//itertion in order to satisfy the TE boundary condition
	//that		d(phi) / d(normal) = 0.
    grid::boundary ret;
    std::ofstream fo(settings::TEbound_path.c_str()); //used if settings::TEbound

    for(uword x = 0; x < w; ++x)
    {
        for(uword y = 0; y < h; ++y)
        {
            if((*this)(x  ,y  ) != ignored) continue;
            std::vector<grid::point2d> neighbors;
            if(y+1 < h && (*this)(x  ,y+1) != ignored) neighbors.push_back(make_point(x,y+1));
            if(x+1 < w && (*this)(x+1,y  ) != ignored) neighbors.push_back(make_point(x+1,y));
            if(static_cast<long long int>(y)-1 >= 0 && (*this)(x  ,y-1) != ignored) neighbors.push_back(make_point(x,y-1));
            if(static_cast<long long int>(x)-1 >= 0 && (*this)(x-1,y  ) != ignored) neighbors.push_back(make_point(x-1,y));

            if(!neighbors.empty())
            {
                ret.push_back( make_pair(make_point(x,y),neighbors) );
                if(settings::TEbound)
                {
                    for(unsigned i = 0; i < neighbors.size(); ++i)
                        fo << neighbors[i].first << ' ' << neighbors[i].second << '\n';
                }
            }
        }//for y
    }//for x
    return ret;
}


